"""Smart Import endpoints: AI extraction, unit conversion, and commit.

Provides contracts for the Smart Import feature:
- POST /smart-import/ai-extract: connect to external AI service (fallback to parser)
- POST /smart-import/compute: convert units and apply loss rate
- POST /smart-import/commit: persist items into DB, associated to project/warehouse
"""

from __future__ import annotations

from datetime import datetime
from typing import Any

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, Field

from app.api.deps import get_current_user
from app.models.user import User
from app.schemas.base import CamelModel
from app.services.smart_import_service import (
    AIProviderError,
    SmartImportAIProvider,
    SmartImportPersistence,
    parse_text,
    validate_items,
)


router = APIRouter()


# ===== Models =====

class SmartImportItem(BaseModel):
    name: str
    quantity: float = Field(..., ge=0)
    unit: str
    target_unit: str | None = None


class ExtractRequest(BaseModel):
    text: str = Field(..., description="Raw text content (CSV-like: name,quantity,unit per line)")


class ExtractResponse(CamelModel):
    items: list[SmartImportItem]
    degraded: bool = False
    message: str | None = None


class ComputeRequest(BaseModel):
    items: list[SmartImportItem]
    loss_rate: float = Field(0.0, ge=0.0, le=100.0, description="Loss rate percent (0-100)")


class ComputeItemOut(CamelModel):
    name: str
    quantity: float
    unit: str
    targetUnit: str
    finalQuantity: float


class ComputeResponse(CamelModel):
    items: list[ComputeItemOut]
    recalcAt: float


class CommitRequest(BaseModel):
    project_id: int = Field(..., ge=1, description="Project ID to associate the import")
    items: list[SmartImportItem]
    loss_rate: float | None = Field(None, ge=0.0, le=100.0, description="Optional loss rate percent (0-100)")
    source_text: str | None = None
    warehouse: str | None = Field(default=None, description="Warehouse/location name or code")
    metadata: dict[str, Any] | None = None


class CommitResponse(CamelModel):
    success: bool
    savedCount: int
    message: str | None = None


# ===== Helpers =====

_SUPPORTED_UNITS = {"kg", "g", "t", "lb"}


def _parse_text(text: str) -> list[SmartImportItem]:
    # Use shared fallback parser to keep behavior consistent
    parsed = parse_text(text)
    return [SmartImportItem(name=it["name"], quantity=it["quantity"], unit=it["unit"], target_unit=it.get("target_unit")) for it in parsed]


def _to_kg(value: float, unit: str) -> float:
    match unit:
        case "kg":
            return value
        case "g":
            return value / 1000.0
        case "t":
            return value * 1000.0
        case "lb":
            return value * 0.45359237
        case _:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unsupported unit: {unit}")


def _from_kg(value: float, unit: str) -> float:
    match unit:
        case "kg":
            return value
        case "g":
            return value * 1000.0
        case "t":
            return value / 1000.0
        case "lb":
            return value / 0.45359237
        case _:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unsupported unit: {unit}")


def _convert(value: float, from_unit: str, to_unit: str) -> float:
    if from_unit == to_unit:
        return value
    return _from_kg(_to_kg(value, from_unit), to_unit)


# ===== Endpoints =====


@router.post("/ai-extract", response_model=ExtractResponse)
async def ai_extract(payload: ExtractRequest, _user: User = Depends(get_current_user)) -> ExtractResponse:
    """Extract items using external AI provider if configured; fallback parser otherwise.

    Degradation is triggered when text contains "FAIL_AI" or provider fails.
    """
    # Preserve explicit degradation trigger for tests
    if "FAIL_AI" in payload.text:
        return ExtractResponse(items=[], degraded=True, message="AI degraded; please use manual entry")

    provider = SmartImportAIProvider.from_settings()
    try:
        ai_items = await provider.extract(payload.text)
        # Map AI items into schema and validate
        mapped = [SmartImportItem(name=it["name"], quantity=float(it["quantity"]), unit=str(it["unit"]).lower(), target_unit=str(it.get("target_unit") or it["unit"]).lower()) for it in ai_items]
        items_valid = validate_items([it.model_dump() for it in mapped])
        items = [SmartImportItem(**it) for it in items_valid]
        return ExtractResponse(items=items, degraded=False, message=None)
    except AIProviderError as e:
        # Fallback to parser
        items = _parse_text(payload.text)
        if not items:
            return ExtractResponse(items=[], degraded=True, message=f"AI unavailable: {e!s}")
        return ExtractResponse(items=items, degraded=False, message="AI fallback parser used")


@router.post("/compute", response_model=ComputeResponse)
async def compute(payload: ComputeRequest, _user: User = Depends(get_current_user)) -> ComputeResponse:
    """Convert units per item target_unit and apply loss rate to produce final quantities."""

    out_items: list[ComputeItemOut] = []
    for it in payload.items:
        target = (it.target_unit or it.unit).lower()
        if it.unit.lower() not in _SUPPORTED_UNITS or target not in _SUPPORTED_UNITS:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unsupported unit in item: {it.unit} -> {target}")
        converted = _convert(it.quantity, it.unit.lower(), target)
        final = converted * (1.0 - payload.loss_rate / 100.0)
        out_items.append(
            ComputeItemOut(
                name=it.name,
                quantity=it.quantity,
                unit=it.unit.lower(),
                targetUnit=target,
                finalQuantity=float(final if final >= 0 else 0.0),
            )
        )

    ts = datetime.now().timestamp()
    return ComputeResponse(items=out_items, recalcAt=ts)


@router.post("/commit", response_model=CommitResponse)
async def commit(payload: CommitRequest, _user: User = Depends(get_current_user)) -> CommitResponse:
    """Persist Smart Import items associated with project/warehouse.

    Performs strict validation and computes final quantities if needed.
    """
    # Strict validation
    normalized = validate_items([it.model_dump() for it in payload.items])

    # Compute final quantities with optional loss_rate
    out_items: list[dict[str, Any]] = []
    loss_rate = float(payload.loss_rate or 0.0)
    for it in normalized:
        target = (it["target_unit"] or it["unit"]).lower()
        if it["unit"].lower() not in _SUPPORTED_UNITS or target not in _SUPPORTED_UNITS:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"Unsupported unit in item: {it['unit']} -> {target}")
        converted = _convert(it["quantity"], it["unit"].lower(), target)
        final = converted * (1.0 - loss_rate / 100.0)
        out_items.append({
            "name": it["name"],
            "quantity": it["quantity"],
            "unit": it["unit"].lower(),
            "target_unit": target,
            "final_quantity": float(final if final >= 0 else 0.0),
        })

    # Persist
    from app.db.session import AsyncSessionLocal

    async with AsyncSessionLocal() as db:
        try:
            saved = await SmartImportPersistence.commit(
                db,
                project_id=payload.project_id,
                items=out_items,
                source_text=payload.source_text,
                warehouse=payload.warehouse,
            )
        except Exception as e:
            raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Commit failed: {e!s}") from e

    return CommitResponse(success=True, savedCount=saved, message=f"Committed: {saved} items")

# End of file
